﻿using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Threading;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Win32;

namespace SpriteSheetsCreator {
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window {
        #region Attributes
        private DispatcherTimer delayedTimer;
        private int currentAnimIndex = 0;
        private bool reversedAnimation = false;
        #endregion

        public MainWindow() {
            InitializeComponent();

            // Set the data context
            DataContext = App.ViewModel;

            // Set ListBox source
            fromImagesListBox.ItemsSource = App.ViewModel.ImagesList;

            // Set animation speed context and binding
            Binding myBinding = new Binding();
            myBinding.Source = App.ViewModel;
            myBinding.Path = new PropertyPath("GuiAnimationSpeedStr");
            myBinding.Mode = BindingMode.TwoWay;
            myBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
            BindingOperations.SetBinding(animSpeedTextBox, TextBox.TextProperty, myBinding);

            myBinding = new Binding();
            myBinding.Source = App.ViewModel;
            myBinding.Path = new PropertyPath("GuiAnimationSpeed");
            myBinding.Mode = BindingMode.TwoWay;
            myBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
            BindingOperations.SetBinding(animSpeedSlider, Slider.ValueProperty, myBinding);

            myBinding = new Binding();
            myBinding.Source = App.ViewModel;
            myBinding.Path = new PropertyPath("AutoreverseAnim");
            myBinding.Mode = BindingMode.TwoWay;
            myBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
            BindingOperations.SetBinding(autoReverseCheckBox, CheckBox.IsCheckedProperty, myBinding);

            myBinding = new Binding();
            myBinding.Source = App.ViewModel;
            myBinding.Path = new PropertyPath("PreviewStretchStyleStr");
            myBinding.Mode = BindingMode.TwoWay;
            myBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
            BindingOperations.SetBinding(imagePreviewStretchStyle, ComboBox.SelectedValueProperty, myBinding);

            myBinding = new Binding();
            myBinding.Source = App.ViewModel;
            myBinding.Path = new PropertyPath("PreviewStretchStyle");
            myBinding.Mode = BindingMode.TwoWay;
            myBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
            BindingOperations.SetBinding(imagePreview, Image.StretchProperty, myBinding);

            // Selected index by default
            imagePreviewStretchStyle.SelectedIndex = 1;

            // if images collection changed - update preview width and display/hide "drop files there" tooltip
            App.ViewModel.ImagesList.CollectionChanged += ImagesList_CollectionChanged;
            ImagesList_CollectionChanged(null, null);
        }

        void ImagesList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
            imagePreviewScroller.Width = 0;
            foreach (ImageViewModel image in App.ViewModel.ImagesList) {
                imagePreviewScroller.Width = Math.Max(image.ImageBitmap.Width, imagePreviewScroller.Width);
            }

            dropFilesThereBorder.Visibility = (App.ViewModel.ImagesList.Count <= 0) ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;
        }

        private void fromImagesListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            if (fromImagesListBox.SelectedItem == null) {
                return;
            }

            imagePreview.Source = ((ImageViewModel)fromImagesListBox.SelectedItem).ImageBitmap;
        }

        /// <summary>
        /// Drag drop started (enter) - set background color according to data - green for files
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void fromImagesListBox_DragEnter(object sender, DragEventArgs e) {
            bool filesHavePngFiles = false;

            // Check that dropped elements are files and contain png
            if (e.Data.GetDataPresent(DataFormats.FileDrop)) {
                string[] filenames = (string[])e.Data.GetData(DataFormats.FileDrop);
                foreach (string filename in filenames) {
                    if (filename.ToLower().EndsWith("png")) {
                        filesHavePngFiles = true;
                    }
                }
            }

            // If data from drag & drop are PNG files, display light green background
            fromImagesListBox.Background = filesHavePngFiles ? Brushes.LightGreen : Brushes.MistyRose;
            e.Effects = filesHavePngFiles ? DragDropEffects.Copy : DragDropEffects.None;

            e.Handled = true;
        }

        /// <summary>
        /// Drag cancelled - reset background to white
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void fromImagesListBox_DragLeave(object sender, DragEventArgs e) {
            // Set back to normal background
            fromImagesListBox.Background = Brushes.White;
        }

        /// <summary>
        /// Drop image files to list box
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void fromImagesListBox_Drop(object sender, DragEventArgs e) {
            // Load dropped images into images list
            SettingsIO.loadImagesFromFilenames((string[])e.Data.GetData(DataFormats.FileDrop));

            // Set back to normal background
            fromImagesListBox.Background = Brushes.White;
        }

        /// <summary>
        /// Launch animation
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PlayButton_Click(object sender, RoutedEventArgs e) {
            if (App.ViewModel.ImagesList.Count <= 0) {
                return;
            }

            // If already started - stop previous animation
            if (delayedTimer != null && delayedTimer.IsEnabled) {
                delayedTimer.Stop();
            }

            // images animation - preparation and start
            delayedTimer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(animSpeedTextBox.Text.toInteger()) };
            reversedAnimation = false;
            delayedTimer.Tick += (sender_, e_) => {
                imagePreview.Source = App.ViewModel.ImagesList[currentAnimIndex].ImageBitmap;
                // animation not reversed
                if (!reversedAnimation) {
                    currentAnimIndex++;
                    if (currentAnimIndex >= App.ViewModel.ImagesList.Count) {
                        if (!App.ViewModel.AutoreverseAnim) {
                            currentAnimIndex = 0;
                        }
                        else {
                            reversedAnimation = true;
                            currentAnimIndex -= 2;
                        }
                    }
                }
                // animation is reversed (auto reverse is set to true and animation reached end of images)
                else {
                    currentAnimIndex--;
                    if (currentAnimIndex < 0) {
                        reversedAnimation = false;
                        currentAnimIndex += 2;
                    }
                }
            };
            delayedTimer.Start();
        }

        private void StopButton_Click(object sender, RoutedEventArgs e) {
            // Stop timer
            if (delayedTimer != null && delayedTimer.IsEnabled) {
                delayedTimer.Stop();
            }
        }

        private void OpenFileButton_Click(object sender, RoutedEventArgs e) {
            // Create an instance of the open file dialog box.
            OpenFileDialog dlg = new OpenFileDialog();

            // Set filter options and filter index.
            dlg.Filter = "PNG images|*.png|All Files|*.*";
            dlg.FilterIndex = 1;

            dlg.Multiselect = true;

            // Call the ShowDialog method to show the dialog box.
            bool? userClickedOK = dlg.ShowDialog();

            // Process input if the user clicked OK.
            if (userClickedOK == true) {
                SettingsIO.loadImagesFromFilenames(dlg.FileNames);
            }
        }

        /// <summary>
        /// Remove selected element(s)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void RemoveItemsButton_Click(object sender, RoutedEventArgs e) {
            // If control key is down, remove all items
            if (Keyboard.Modifiers == ModifierKeys.Control) {
                App.ViewModel.ImagesList.Clear();
                return;
            }

            // Control key not down - remove one image at a time - if no image selected, exit
            if (fromImagesListBox.SelectedIndex < 0) {
                return;
            }

            // Used to select the next one after operation is over
            int prvSelectedIndex = fromImagesListBox.SelectedIndex;

            // Get list of elements to remove
            var imagesToRemove = fromImagesListBox.SelectedItems.Cast<ImageViewModel>().ToList();
            var allImages = App.ViewModel.ImagesList.ToList<ImageViewModel>();

            // Clear list and re add all images but selected ones
            App.ViewModel.ImagesList.Clear();
            foreach (ImageViewModel image in allImages) {
                if (!imagesToRemove.Contains(image)) {
                    App.ViewModel.ImagesList.Add(image);
                }
            }

            // Select next item (or previous one if it was the last one)
            fromImagesListBox.SelectedIndex = Math.Min(App.ViewModel.ImagesList.Count - 1, prvSelectedIndex);
            fromImagesListBox.Focus();
        }

        private void MoveUpButton_Click(object sender, RoutedEventArgs e) {
            if (fromImagesListBox.SelectedIndex <= 0) {
                return;
            }

            // Used to get the target position of the operation
            int prvSelectedIndex = fromImagesListBox.SelectedIndex;

            ImageViewModel imageToMove = App.ViewModel.ImagesList.ElementAt(fromImagesListBox.SelectedIndex);
            App.ViewModel.ImagesList.RemoveAt(fromImagesListBox.SelectedIndex);
            App.ViewModel.ImagesList.Insert(prvSelectedIndex - 1, imageToMove);

            // Select item again
            fromImagesListBox.SelectedIndex = prvSelectedIndex - 1;
            fromImagesListBox.Focus();
        }

        private void MoveDownButton_Click(object sender, RoutedEventArgs e) {
            if (fromImagesListBox.SelectedIndex < 0 || fromImagesListBox.SelectedIndex > (App.ViewModel.ImagesList.Count - 1)) {
                return;
            }

            // Used to get the target position of the operation
            int prvSelectedIndex = fromImagesListBox.SelectedIndex;

            ImageViewModel imageToMove = App.ViewModel.ImagesList.ElementAt(fromImagesListBox.SelectedIndex);
            App.ViewModel.ImagesList.RemoveAt(fromImagesListBox.SelectedIndex);
            App.ViewModel.ImagesList.Insert(prvSelectedIndex + 1, imageToMove);

            // Select item again
            fromImagesListBox.SelectedIndex = prvSelectedIndex + 1;
            fromImagesListBox.Focus();
        }

        private void SaveButton_Click(object sender, RoutedEventArgs e) {
            SaveFileDialog dlg = new SaveFileDialog();
            dlg.DefaultExt = ".png";
            dlg.Filter = "PNG images|*.png|All Files|*.*";
            if (dlg.ShowDialog() == true) {
                SettingsIO.saveImageResult(dlg.FileName);
            }
        }

        #region Window events
        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) {
            SettingsIO.saveGuiParams();
        }
        #endregion

        private void DefaultSettingsButton_Click(object sender, RoutedEventArgs e) {
            App.ViewModel.GuiAnimationSpeed = 33;
            App.ViewModel.AutoreverseAnim = false;
        }
    }
}
